package ar.edu.sccs.model.cbr;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;

import ar.edu.sccs.dao.DaoModule;
import ar.edu.sccs.entities.Case;
import ar.edu.sccs.entities.Dimension;
import ar.edu.sccs.model.similarity.SimilarityModule;
import ar.edu.sccs.monitoring.exception.SccsGenericException;
import ar.edu.sccs.util.Constants;
import ar.edu.sccs.util.SvnHelper;

public class CbrModule {

	static Logger logger = Logger.getLogger(CbrModule.class);

	private DaoModule daoModule = new DaoModule();
	private SimilarityModule similarityModule = SimilarityModule.getInstance();

	public List<Case> searchResult(Case targetCase, List<Case> semanticCases) throws SccsGenericException {
		logger.info("CbrModule.searchResult: " + semanticCases.toString());
		List<Case> resultCases = new ArrayList<Case>();

		for (Case semanticCase : semanticCases) {
			List<Case> retrievedCases = daoModule.getCases(semanticCase);

			for (Case retrievedCase : retrievedCases) {
				// Calcula la simimlitud de cada una de las dimensiones del caso
				// recuperado
				calculateSimilarity(targetCase, retrievedCase);

				// Calcula la simimlitud del conjunto de las dimensiones del
				// caso recuperado
				calculateCaseSimilarity(targetCase, retrievedCase);

				resultCases.add(retrievedCase);
			}
		}

		// Ordena por grado de similitud los casos recuperado
		return sortResults(resultCases);
	}

	private Case calculateSimilarity(Case targetCase, Case retrievedCase) {
		logger.debug("CbrModule.calculateSimilarity");

		similarityModule.calculateSimilarity(targetCase, retrievedCase);
		return retrievedCase;
	}

	private Case calculateCaseSimilarity(Case targetCase, Case retrievedCase) {
		logger.debug("CbrModule.calculateCaseSimilarity");
		double similityValue = 0;
		double weightValue = 0;
		int quantityValues = 0;

		for (Iterator<String> keys = targetCase.getDimensions().keySet().iterator(); keys.hasNext();) {
			Dimension retrieved;
			String key = keys.next();
			Dimension dim = targetCase.getDimensions().get(key);

			if (dim.getWeight() != 0) {
				if ((retrieved = retrievedCase.getDimensions().get(key)) != null)
					similityValue += retrieved.getSimilarity() * dim.getWeight();

				weightValue += dim.getWeight();
				quantityValues++;
			}
		}

		retrievedCase.setSimilarity(similityValue / weightValue);
		logger.info("CbrModule.calculateCaseSimilarity: Se obtiene " + retrievedCase.getSimilarity() + " como similitud total del caso");
		return retrievedCase;
	}

	private List<Case> sortResults(List<Case> resultCases) {
		logger.debug("CbrModule.sortResults");

		Collections.sort(resultCases, new SimilarityComparator());
		if (resultCases.size() < Constants.Cbr.LIMIT_RESULT) {
			logger.info("CbrModule.sortResults: Se retornan " + resultCases.size() + " resultados");
			return resultCases;
		} else {
			logger.info("CbrModule.sortResults: Se retornan " + resultCases.size() + " resultados");
			return resultCases.subList(0, Constants.Cbr.LIMIT_RESULT);
		}
	}

	public class SimilarityComparator implements Comparator<Case> {

		public int compare(Case o1, Case o2) {
			Double double1 = o1.getSimilarity();
			Double double2 = o2.getSimilarity();

			return double1.compareTo(double2);
		}
	}

	public File recoverCode(Case selectedCase) {
		return SvnHelper.recoverCode(selectedCase.getScmUrl());
	}
}
